object Demo08Func2 {
  // func1函数的类型
  // String => Int
  def func1(str: String): Int = {
    str.toInt + 100
  }

  // String => Int
  def func2(str: String): Int = {
    str.toInt + 200
  }

  // 定义一个函数funcX 能够接收 ”参数为String返回值为Int“ 的函数
  // (String=>Int) => Unit
  def funcX(func: String => Int): Unit = {
    val i: Int = func("100")
    println(i)
  }

  // Int => Int
  def func3(i: Int): Int = {
    i + 300
  }

  // Int => Long
  def func4(i: Int): Long = {
    i + 300L
  }

  /**
   * 匿名函数（类似Java中的lambda表达式）
   * (参数名1:参数类型,参数名2:参数类型,参数名3:参数类型) => {函数体}
   */

  // 使用匿名函数实现func1的逻辑
  (str: String) => {
    str.toInt + 100
  }

  // 匿名函数也可以拥有名称
  val func5: String => Int = (str: String) => {
    str.toInt + 200
  }


  def main(args: Array[String]): Unit = {
    /**
     * 函数式编程（面向函数编程、高阶函数）
     *
     */

    /**
     * 面向对象编程：将对象传来传去，可以作为参数传给某个函数，也可以作为返回值返回，需要注意参数类型、返回值类型的限制
     * 面向函数编程：将函数传来传去，可以作为参数传给某个函数，也可以作为返回值返回，需要注意参数类型、返回值类型的限制
     *
     * 函数的类型跟什么无关？
     * 1、跟def关键字无关
     * 2、跟函数名无关
     * 3、跟参数的名称无关
     * 4、跟函数体没有直接关系
     *
     * 函数的类型 跟 参数的类型（参数的顺序、个数、类型）、返回值的类型有关
     *
     * 怎么去描述函数的类型？
     * 参数类型 => 返回值类型
     * (参数1的类型,参数2的类型,参数3的类型......) => 返回值类型
     *
     * 函数式编程：
     * 1、以函数作为参数（最常用的、重点）
     * 2、以函数作为返回值
     *
     */

    funcX(func1) // 200
    funcX(func2) // 300
    //    funcX(func3) // 传入的函数的类型 跟funcX定义的参数的类型不匹配
    //    funcX(func4) // 传入的函数的类型 跟funcX定义的参数的类型不匹配

    // 使用匿名函数
    funcX((str: String) => {
      str.toInt + 100
    }) // 200

    //  String => Int
    funcX(func5)


    /**
     * 匿名函数的省略规则
     * 1、如果代码只有一行 花括号可以省略
     * 2、如果匿名函数作为另一个函数的参数传入时 匿名函数的参数类型可以省略
     * 3、如果匿名函数只有一个参数 括号可以省略 （在条件2的基础上）
     * 4、如果匿名函数的参数只使用了一次 则可以用下划线替代 但需要把 => 以及左边的部分（即参数的声明）省略
     */
    funcX((str: String) => {
      str.toInt + 203
    })

    funcX((str: String) => str.toInt + 203)

    funcX((str) => str.toInt + 203)

    funcX(str => str.toInt + 203) // 省略到这步就够了

    funcX(_.toInt + 203) // 可读性很低


  }

}
